home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume91 / midi / casio / part01 / casio.c next >
C/C++ Source or Header  |  1991-03-28  |  11KB  |  421 lines

  1. /* casio - download sample data from a casio fz-1 */
  2.  
  3. /* Copyright (C) 1988 Hackercorp.   All Rights Reserved */
  4.  
  5. #include <libraries/dos.h>
  6. #include <clib/macros.h>
  7. #include <midi/midi.h>
  8. #include <functions.h>
  9. #include <stdio.h>
  10. #include <devices/timer.h>
  11.  
  12. #include "casio.h"
  13. #include "prototypes.h"
  14.  
  15. char *program_name = "casio fz1 download program";
  16.  
  17. #define CheckMsg(m) (m->mp_MsgList.lh_Head->ln_Succ != 0)
  18.  
  19. extern struct timerequest *timer_request;
  20.  
  21. /* message ids for fz-1 system exclusive messages */
  22. #define OPEN_MIDI_MSG 0x70
  23. #define CLOSE_MIDI_MSG 0x71
  24. #define OK_MIDI_MSG 0x72
  25. #define ERR_MIDI_MSG 0x73
  26. #define DATA_MIDI_MSG 0x74
  27. #define EFFECT_MIDI_MSG 0x76
  28. #define REMOTE_MIDI_MSG 0x7f
  29.  
  30. /* additional messages I define as responses from its_an_fz1_message */
  31. #define NOT_A_FZ_MESSAGE 0
  32. #define NOT_THE_REQUESTED_FZ_CHANNEL 1
  33. #define DATA_ERROR 2
  34.  
  35. /* states for the i/o loop */
  36. #define WAITING_FOR_OPEN 0
  37. #define READING_FZ_DATA 1
  38.  
  39. #define MAX_RETRIES 10
  40.  
  41. #define CAPTURE_BUFFER_SIZE 16384
  42. UBYTE capture_buffer[CAPTURE_BUFFER_SIZE];
  43.  
  44. void *MidiBase = NULL;
  45. void *IntuitionBase = NULL;
  46.  
  47. struct MDest *dest = NULL;
  48. struct MSource *source = NULL;
  49.  
  50. struct MRoute *route_a = NULL;
  51. struct MRoute *route_b = NULL;
  52.  
  53. UBYTE *msg;            /* buffer this in case we get shut down before freeing the current message */
  54.  
  55. int capture_position = 0;
  56.  
  57. void midi_cleanup(void)
  58. {
  59.     /* if (msg) 
  60.         FreeMidiMsg(msg); */
  61.  
  62.     if (IntuitionBase)
  63.         CloseLibrary(IntuitionBase);
  64.  
  65.     if (dest) 
  66.         DeleteMDest(dest);
  67.     if (source)
  68.         DeleteMSource(source);
  69.  
  70.     if (route_a)
  71.         DeleteMRoute(route_a);
  72.     if (route_b)
  73.         DeleteMRoute(route_b);
  74.  
  75.     if (MidiBase) 
  76.         CloseLibrary(MidiBase);
  77. }
  78.  
  79. /* if channel is -1, any channel is ok (within FZ-1 System Exclusive message) */
  80.  
  81. /* validate the incoming message, including all sorts of stuff about the header
  82.  * and anything we know specifically within the message type as well */
  83.  
  84. UBYTE its_an_fz1_message(UBYTE *msg, int requested_channel)
  85. {
  86.     int channel, length, expected_length;
  87.     struct FZ1_Message *p;
  88.  
  89.     p = (struct FZ1_Message *)msg;
  90.  
  91.     /* compare first bytes of message,
  92.        extract channel number and verify,
  93.        return function code
  94.      */
  95.     length = MidiMsgLength(msg);
  96.  
  97.     if ((p->casio_id != 0x44) || (p->fz1_id != 0x200)
  98.         || ((p->encoded_channel & 0xf0) != 0x70))
  99.             return(NOT_A_FZ_MESSAGE);
  100.  
  101.     channel = (p->encoded_channel & 0x0f);
  102.     if ((requested_channel != -1) && (channel != requested_channel))
  103.         return(NOT_THE_REQUESTED_FZ_CHANNEL);
  104.  
  105.     switch(p->command)
  106.     {
  107.         case OPEN_MIDI_MSG:
  108.             expected_length = 16;
  109.             break;
  110.  
  111.         case CLOSE_MIDI_MSG:
  112.             expected_length = 7;
  113.             break;
  114.  
  115.         case DATA_MIDI_MSG:
  116.             expected_length =  136;
  117.             if (length == expected_length && (p->body.datamsg.checksum != calc_checksum(p)))
  118.             {
  119.                  fprintf(stderr,"checksum error, expected %x, saw %x\n",
  120.                      p->body.datamsg.checksum,calc_checksum(p));
  121.                  return(DATA_ERROR);
  122.             }
  123.             break;
  124.  
  125.         /* haven't seen any of these yet */
  126.         case OK_MIDI_MSG:
  127.         case ERR_MIDI_MSG:
  128.         case EFFECT_MIDI_MSG:
  129.         case REMOTE_MIDI_MSG:
  130.             panic("got a message I don't know how to handle");
  131.  
  132.         /* it wasn't a message we understood yet it had all the makings of
  133.          * being one, it must be garbled */
  134.         default:
  135.             return(DATA_ERROR);
  136.     }
  137.     if (length != expected_length)
  138.         return(DATA_ERROR);
  139.  
  140.     return(p->command);
  141. }
  142.  
  143. void commune_with_fz1(struct MSource *source, struct MDest *dest, int channel, int outfd)
  144. {
  145.     long len;
  146.     UBYTE *ip;
  147.     int command, retry_count = 0;
  148.     int state = WAITING_FOR_OPEN;
  149.     ULONG waitflags;
  150.     int packets_received = 0, error_count = 0, done = 0;
  151.     struct FZ1_Message *p;
  152.  
  153.     /* wait for a message or control-C or "done" control var to be set */
  154.     while ((!((waitflags = Wait(-1)) & SIGBREAKF_CTRL_C)) && !done)
  155.     {     
  156.         /* if ((waitflags & SIGBREAKF_CTRL_D) && (state = READING_FZ_DATA))
  157.         {
  158.             printf("saw a ^d\n");
  159.             goto data_error;
  160.         } */
  161.  
  162.         /* while there are messages to process, process messages */
  163.         while (!done && (msg = GetMidiMsg(dest)))
  164.         {        
  165.             /* discard all but system exclusive data - should be done
  166.              * by the route, check someday to see if we can find our
  167.              * route and change it if it isn't */
  168.             if (*msg != MS_SYSEX)
  169.                 goto message_done;
  170.  
  171.             p = (struct FZ1_Message *)msg;
  172.             command = its_an_fz1_message((UBYTE *)p,channel);
  173.  
  174.             if (state == WAITING_FOR_OPEN)
  175.             {
  176.                 if (command != OPEN_MIDI_MSG)
  177.                     panic("out of sync - looking for 'Open' from FZ1");
  178.  
  179.                 /* we need to specify a channel ID in our messages
  180.                  * if we're not looking for a specific one, reply
  181.                  * with the one we got in the first message */
  182.                 if (channel == -1)
  183.                     channel = p->encoded_channel & 0x0f;
  184.                 state = READING_FZ_DATA;
  185.                 dump_open(p);
  186.                 send_fz(source,channel,OK_MIDI_MSG);
  187.                 goto message_done;
  188.             }
  189.  
  190.             if (state == READING_FZ_DATA)
  191.             {
  192.                 abort_timeout_timer();
  193.                 switch(command)
  194.                 {
  195.                     case DATA_MIDI_MSG:
  196.                         send_fz(source,channel,OK_MIDI_MSG);
  197.                         start_timeout_timer();
  198.                         packets_received++;
  199.                         if ((packets_received % 10) == 0)
  200.                         {
  201.                             printf("%4d ok \r",packets_received);
  202.                             fflush(stdout);
  203.                         }
  204.                         capture(p,outfd);
  205.                         retry_count = 0;
  206.                         break;
  207.  
  208.                     case DATA_ERROR:
  209.                     data_error:
  210.                         send_fz(source,channel,ERR_MIDI_MSG);
  211.                         start_timeout_timer();
  212.                         printf("%4d err\r",packets_received);
  213.                         fflush(stdout);
  214.                         error_count++;
  215.                         if (++retry_count > MAX_RETRIES)
  216.                             panic("exceeded max retries for a packet - transfer failed");
  217.                         break;
  218.  
  219.                     /* we get this when the fz is done */
  220.                     case CLOSE_MIDI_MSG:
  221.                         fprintf(stderr,"casio is done\n");
  222.                         done = 1;
  223.                         break;
  224.  
  225.                     default:
  226.                         fprintf(stderr,"unknown command = 0x%x\n",command);
  227.                         if (state = READING_FZ_DATA)
  228.                         {
  229.                             fprintf(stderr,"will try treating it as a data error\n");
  230.                             goto data_error;
  231.                         }
  232.                         else
  233.                             panic("unknown command and not in 'read' mode");
  234.                 }
  235.                 goto message_done;
  236.             }
  237.  
  238.             panic("state variable fried");
  239.  
  240.             message_done: FreeMidiMsg(msg);                /* free it */
  241.         }
  242.  
  243.         if (CheckMsg(timer_request->tr_node.io_Message.mn_ReplyPort) && 
  244.          (GetMsg(timer_request->tr_node.io_Message.mn_ReplyPort)) )
  245.         {
  246.             if (state == READING_FZ_DATA)
  247.             {
  248.                 send_fz(source,channel,ERR_MIDI_MSG);
  249.                 printf("%4d tmo\r",packets_received);
  250.                 fflush(stdout);
  251.                 error_count++;
  252.                 if (++retry_count > MAX_RETRIES)
  253.                     panic("exceeded max retries for a packet - transfer failed");
  254.             }
  255.         }
  256.     }
  257.     if (capture_position != 0)
  258.     {
  259.         if (write(outfd,capture_buffer,capture_position) != capture_position)
  260.             perror("output file");
  261.     }
  262.     printf("total packets received %d (%d retries)\n",packets_received,error_count);
  263. }
  264.  
  265. void send_fz(struct MSource *source, int channel_id, int message_id)
  266. {
  267.     static UBYTE buf[] = {0xf0, 0x44, 0x02, 0x00, 0x70,0x00,0xf7};
  268.     buf[4] |= channel_id;
  269.     buf[5] = message_id;
  270.     PutMidiMsg(source,buf);
  271. }
  272.  
  273. int calc_checksum(struct FZ1_Message *p)
  274. {
  275.     UBYTE *ip, lownibble, hinibble;
  276.     int len, running_sum;
  277.  
  278.     /* I assume the message has already been validated as a Casio FZ-1 
  279.      * data message
  280.      */
  281.  
  282.     ip = &p->body.datamsg.data[0];
  283.  
  284.     for (len = 0, running_sum = 0; len < 128; len++)
  285.     {
  286.         running_sum += *ip++;
  287.     }
  288.     return((0 - running_sum) & 0x7f);
  289. }
  290.  
  291. void capture(struct FZ1_Message *p, int outfd)
  292. {
  293.     UBYTE *ip, lownibble, hinibble;
  294.     int len;
  295.  
  296.     ip = &p->body.datamsg.data[0];
  297.  
  298.     for (len = 0; len < 64; len++)
  299.     {
  300.         lownibble = *ip++;
  301.         hinibble = *ip++;
  302.         capture_buffer[capture_position++] = (hinibble << 4) | lownibble;
  303.         if (capture_position >= CAPTURE_BUFFER_SIZE)
  304.         {
  305.             if (write(outfd,capture_buffer,CAPTURE_BUFFER_SIZE) != CAPTURE_BUFFER_SIZE)
  306.             {
  307.                 perror("output file");
  308.             }
  309.             capture_position = 0;
  310.         }
  311.     }
  312. }
  313.  
  314. struct MRouteInfo myrouteinfo =
  315. {
  316.     MMF_SYSEX,0xffff,0,0,{0},{0}
  317. };
  318.  
  319. main(int argc,char *argv[])
  320. {
  321.  
  322.     char *sname, *dname, *filename;
  323.     int outfd;
  324.     int wanted_channel = -1;
  325.  
  326.     fprintf(stderr,"ready to talk to casio, the routes are done automatically from\n");
  327.     fprintf(stderr,"midi_in if it's available, else MidiIn and to MidiOut.\n");
  328.     fprintf(stderr,"I download and exit when done.  I automatically do retries.\n");
  329.     fprintf(stderr,"Nonetheless, I may 'hang' when uploading.  This is soft.\n");
  330.     fprintf(stderr,"Hit ^D to retry.  Or hit ^C, I'll exit.\n");
  331.     fprintf(stderr,"I write to the file on the fly (every 256 packets), so watch it.\n");
  332.     if (argc != 2)
  333.     {
  334.         fprintf(stderr,"usage: casio filename, routes are automatic, then\n");
  335.         fprintf(stderr," select FZ output device as 'midi' and do a voice save\n");
  336.         exit(1);
  337.     }
  338.  
  339.     sname = "FZ1out";
  340.     dname = "FZ1in";
  341.     filename = argv[1];
  342.  
  343.     if ((outfd = creat(filename,0666)) == -1)
  344.     {
  345.         fprintf(stderr,"couldn't create output file\n");
  346.         perror(filename);
  347.         exit(2);
  348.     }
  349.  
  350.     if (!(IntuitionBase = OpenLibrary("intuition.library",0)))
  351.     {
  352.         fprintf(stderr,"can't open intuition.library\n");
  353.         goto clean;
  354.     }
  355.  
  356.     add_cleanup(midi_cleanup);
  357.  
  358.     if (!(MidiBase = OpenLibrary(MIDINAME,MIDIVERSION))) 
  359.     {
  360.         fprintf(stderr,"can't open midi.library\n");
  361.         goto clean;
  362.     }
  363.  
  364.     /* create our public source node */
  365.     if (!(source = CreateMSource(sname,NULL)))
  366.     {
  367.         fprintf(stderr,"can't create Source port %s\n",sname);
  368.         goto clean;
  369.     }
  370.  
  371.     /* create our dest node (public) */
  372.     if (!(dest = CreateMDest(dname,NULL))) 
  373.     {
  374.         fprintf(stderr,"can't create Dest port %s\n",dname);
  375.         goto clean;
  376.     }
  377.  
  378.     route_a = MRoutePublic("midi_in",dname,&myrouteinfo);
  379.     if (!route_a)
  380.         route_a = MRoutePublic("MidiIn",dname,&myrouteinfo);
  381.  
  382.     route_b = MRoutePublic(sname,"MidiOut",&myrouteinfo);
  383.  
  384.     init_timer();
  385.  
  386.     commune_with_fz1(source,dest,wanted_channel,outfd);       /* process until shutdown */
  387.  
  388. clean:
  389.     close(outfd);
  390.     cleanup();
  391.     exit(0);
  392. }
  393.  
  394. void _abort(void)       /* abort routine called when CTRL-C is hit (Aztec) */
  395. {
  396.     /* fflush(stdout); cleanup(); exit(1); */
  397. } /* we're polling it in the loop */
  398.  
  399. void dump_open(struct FZ1_Message *p)
  400. {
  401.     unsigned int nblocks;
  402.  
  403.     printf("dump of Open MIDI message received from Casio FZ-1\n");
  404.     printf("channel %d, status %d, ",p->encoded_channel & 0x0f,p->body.openmsg.status);
  405.     printf("banks %d, voices %d, ",p->body.openmsg.banks,p->body.openmsg.voices);
  406.  
  407.     if(p->body.openmsg.status != 1 || p->body.openmsg.banks != 0 
  408.         || p->body.openmsg.voices != 1)
  409.         panic("I don't know how to do anything other than save a single voice");
  410.  
  411.     nblocks = (p->body.openmsg.nblocks_hinibble << 12) 
  412.         | (p->body.openmsg.nblocks_next_to_hinibble << 8) 
  413.         | (p->body.openmsg.nblocks_next_to_lonibble << 4) 
  414.         | p->body.openmsg.nblocks_lonibble;
  415.     printf("nblocks %d, ",nblocks);
  416.     printf("edit bank %d, edit voice %d\n",p->body.openmsg.edit_bank,p->body.openmsg.edit_voice);
  417. }
  418.  
  419. /* end of casio.c */
  420.  
  421.